﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Threading.Tasks;

namespace OutillageTests
{
    /// <summary>
    /// Fournit des fonctionnalités de création de rapport, sous forme de fichier .csv
    /// </summary>
    public class ReportingManager
    {
        private Dictionary<Type, Dictionary<MethodInfo, bool>> cartoMethodes;
        private StreamWriter sW;

        // Délégué de conversion d'un dictionnaire de paramètres, en chaîne
        delegate string ParamsStringBuilder(ParameterInfo[] parameters);

        // Délégué de conversion de testabilité de méthode (booleen => string)
        delegate string TestableMethodStringBuilder(bool methodeTestable);
        
        /// <summary>
        /// Constructeur
        /// </summary>
        /// <param name="file">Chemin complet du CSV</param>
        public ReportingManager(string file, Dictionary<Type, Dictionary<MethodInfo, bool>> uneCarto)
        {
            cartoMethodes = uneCarto;
            sW = new StreamWriter(file);

            ParamsStringBuilder csvStringBuilder = buildParametersString;
            ajouterLigne("Namespace;Classe;Methode;Type de retour;Testable;Parametres;");
            ajouterLigne(";;;;;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type;Nom;Type");
        }

        /// <summary>
        /// Construction du rapport en fonction de la cartographie
        /// </summary>
        public void CreerRapport()
        {
            try
            {
                Parallel.ForEach(cartoMethodes, elementCarto =>
                    {
                        foreach (KeyValuePair<MethodInfo, bool> element in elementCarto.Value)
                        {
                            ajouterLigneRapport(elementCarto.Key, element.Key, element.Value);
                        }
                    });

                sW.Close();
            }
            catch (Exception e)
            {
                throw new Exception("Une erreur est survenue lors de la création du rapport", e);
            }
        }

        /// <summary>
        /// Alimente le rapport avec une nouvelle méthode
        /// </summary>
        /// <param name="classType"></param>
        /// <param name="method"></param>
        /// <param name="testable"></param>
        private void ajouterLigneRapport(Type classType, MethodInfo method, bool testable)
        {
            try
            {
                if (classType != null && method != null)
                {
                    TestableMethodStringBuilder csvTestableMethodStringBuilder = buildTestableMethodString;
                    ParamsStringBuilder csvStringBuilder = buildParametersString;

                    ajouterLigne(classType.Namespace, classType.Name, method.Name, method.ReturnType.ToString(),
                        csvTestableMethodStringBuilder(testable), csvStringBuilder(method.GetParameters()));
                }
            }
            catch (Exception e)
            {
                throw new Exception("Une erreur est survenue lors du traitement d'une méthode", e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="unNamespace">Namespace associé à la méthode</param>
        /// <param name="uneClasse">Classe contenant la déclaration de méthode (ancêtres non gérés)</param>
        /// <param name="uneMethode">Nom de la méthode</param>
        /// <param name="unRetour">Type de retour (vide pour les void)</param>
        /// <param name="methodeTestable">"OUI" ou "NON"</param>
        /// <param name="parameters">Dictionnaire formaté en chaîne CSV de noms de types et noms de variables, paramètres de la méthode</param>
        private void ajouterLigne(string unNamespace, string uneClasse, string uneMethode,
            string unRetour, string methodeTestable, string formatedParameters)
        {
            ajouterLigne(unNamespace + ";" + uneClasse + ";" + uneMethode + ";" +
                unRetour + ";" + methodeTestable + ";" + formatedParameters);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ligneComplete">Ligne formatée CSV</param>
        private void ajouterLigne(string ligneComplete)
        {
            lock (this)
            {
                sW.WriteLine(ligneComplete);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parmeters">Tableau de ParameterInfo[] contenant les informations de parmètres</param>
        /// <returns></returns>
        private static string buildParametersString(ParameterInfo[] parmeters)
        {
            string result = string.Empty;
            foreach (ParameterInfo infoParam in parmeters)
	        {
                if (result.Length > 0) result += ";";
                result += infoParam.Name + ";" + infoParam.ParameterType.ToString();
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="testableMethod">Booléen indiquant si la méthode est testable, d'après les critères du ClassParser</param>
        /// <returns></returns>
        private static string buildTestableMethodString(bool testableMethod)
        {
            if (testableMethod) return "OUI";
            else return "NON";
        }
    }
}
